/**************************************************************************
** Qt Creator license header template
**   Special keywords: Programmer 2012/5/11 2012
**   Environment variables: 
**   To protect a percent sign, use '%'.
**************************************************************************/
#include <vector>
#include <fstream>
#include <iosfwd>

#include <wrap/io_trimesh/export_ply.h>

#include "progressivemesh.h"

void simplify(CMeshO &m,int  TargetFaceNum, bool Selected, ProgressiveMeshParameter &pp, vcg::CallBackPos *cb){
    vcg::math::Quadric<double> QZero;
    QZero.SetZero();
    QuadricTemp TD(m.vert,QZero);
    Helper::TDp()=&TD;

    if(Selected) // simplify only inside selected faces
    {
        // select only the vertices having ALL incident faces selected
        vcg::tri::UpdateSelection<CMeshO>::VertexFromFaceStrict(m);

        // Mark not writable un-selected vertices
        CMeshO::VertexIterator  vi;
        for(vi=m.vert.begin();vi!=m.vert.end();++vi) if(!(*vi).IsD())
        {
            if(!(*vi).IsS()) (*vi).ClearW();
            else (*vi).SetW();
        }
    }

    if(pp.PreserveBoundary && !Selected)
    {
        pp.FastPreserveBoundary=true;
        pp.PreserveBoundary = false;
    }

    if(pp.NormalCheck) pp.NormalThrRad = M_PI/4.0;


    vcg::LocalOptimization<CMeshO> DeciSession(m,&pp);
    cb(1,"Initializing simplification");
    DeciSession.Init<MeshSimplification>();

    if(Selected)
        TargetFaceNum= m.fn - (m.sfn-TargetFaceNum);
    DeciSession.SetTargetSimplices(TargetFaceNum);
    DeciSession.SetTimeBudget(0.1f); // this allow to update the progress bar 10 time for sec...
    //  if(TargetError< numeric_limits<double>::max() ) DeciSession.SetTargetMetric(TargetError);
    //int startFn=m.fn;
    int faceToDel=m.fn-TargetFaceNum;
    while( DeciSession.DoOptimization() && m.fn>TargetFaceNum )
    {
        cb(100-100*(m.fn-TargetFaceNum)/(faceToDel), "Simplifying...");
    };

    DeciSession.Finalize<MeshSimplification>();

    if(Selected) // Clear Writable flags
    {
        CMeshO::VertexIterator  vi;
        for(vi=m.vert.begin();vi!=m.vert.end();++vi)
            if(!(*vi).IsD()) (*vi).SetW();
    }
}

//find the faces from vl to vr in CCW.
void FindEdgeSet(std::vector<vcg::face::VFIterator<CFaceO> >& es,VSplit* s){
    CVertexO* nextv = s->vl;
    vcg::face::VFIterator<CFaceO> vfi(s->v);

    //clear Visited flag
    for(; !vfi.End(); ++vfi)
        vfi.f->ClearV();

    while(true){
        vfi.f = s->v->VFp();
        vfi.z = s->v->VFi();
        for(; !vfi.End();++vfi){
            if(!vfi.f->IsV() && vfi.f->V((vfi.z+1)%3)==nextv){
                es.push_back(vfi);
                vfi.f->SetV();
                nextv = vfi.f->V((vfi.z+2)%3);
                break;
            }
        }
        if(s->vr==nextv){
            break;
        }

    }
}

void restore(CMeshO& m,ProgressiveMeshParameter& pp,vcg::CallBackPos* cb,std::string fullPathName){

//
    std::string tmpstr = fullPathName;
    tmpstr.insert(tmpstr.length()-4,"_base");
    tmpstr.replace(tmpstr.length()-3,3,"ply");
    cb(0,"save the base file...");
    vcg::tri::io::ExporterPLY<CMeshO>::Save(m,tmpstr.c_str(),false);

    std::vector<vcg::face::VFIterator<CFaceO> > es;

    VSplit* s = NULL;
    CMeshO::VertexPointer vNew = NULL;//,vl,vr;

    CFaceO* f1 = NULL;
    CFaceO* f2 = NULL;


    int total = pp.vSplits.size();
    int current = 0;


    unsigned int j = 0;
    vcg::SimpleTempData<CMeshO::VertContainer,unsigned int> indices(m.vert);
    for(CMeshO::VertexIterator vi = m.vert.begin();vi != m.vert.end(); vi++){
        if((*vi).IsD()) continue;
        indices[vi] = j;
        j++;
    }

    std::vector<Record> recordsToFile;

    Record record;
    //for test
    size_t num = pp.vSplits.size();
    while(!pp.vSplits.empty()){

        s = pp.getRemoveSplit();
        assert(!s->v->IsD() && !s->vl->IsD() && !s->vr->IsD());

        //add a new Vertex
        vNew = pp.getRemoveVertex();
        assert(vNew->IsD());
        vNew->ClearD();
        vNew->P() = s->v0;
        vNew->VFi() = -1;
        vNew->VFp() = NULL;
        m.vn++;

        indices[vNew] = j;
        j++;
        record.v0 = s->v0;
        record.v1 = s->v1;
        record.v = indices[s->v];
        record.vl = indices[s->vl];
        record.vr = indices[s->vr];
        recordsToFile.push_back(record);


        es.clear();
        FindEdgeSet(es,s);
        assert(!es.empty());

        //setting the VF topo for vNew
        for(std::vector<vcg::face::VFIterator<CFaceO> >::iterator i=es.begin(); i!=es.end();i++){

            vcg::face::VFDetach(*((*i).f),(*i).z);
            (*i).f->V((*i).z) = vNew;
            (*i).f->VFp((*i).z) = 0;
            (*i).f->VFi((*i).z) = -1;
            vcg::face::VFAppend((*i).f,(*i).z);
        }

        //add two new faces.
        f1 = pp.getRemoveFace();
        f2 = pp.getRemoveFace();
        assert(f1->IsD() && f2->IsD());
        f1->ClearD();
        f2->ClearD();
        m.fn += 2;

        f1->V(0) = vNew;
        f1->V(1) = s->v;
        f1->V(2) = s->vl;
        vcg::face::VFAppend<CFaceO>(f1,0);
        vcg::face::VFAppend<CFaceO>(f1,1);
        vcg::face::VFAppend<CFaceO>(f1,2);
        f2->V(0) = vNew;
        f2->V(1) = s->vr;
        f2->V(2) = s->v;
        vcg::face::VFAppend<CFaceO>(f2,0);
        vcg::face::VFAppend<CFaceO>(f2,1);
        vcg::face::VFAppend<CFaceO>(f2,2);

        s->v->P() = s->v1;
        current++;
        cb(100*current/total,"restoring...");
    }

    cb(0,"restore completed.");
    assert(recordsToFile.size() == num);
    fullPathName.replace(fullPathName.length()-3,3,"prm");
    saveRecordsToFile(recordsToFile,fullPathName,cb);
    recordsToFile.clear();
}

void saveRecordsToFile(std::vector<Record>& records,std::string filePathName,vcg::CallBackPos cb){
    std::ofstream fout(filePathName.c_str());
    if(!fout.is_open()){
        cb(0,"Open file failed when saving..");
        return;
    }
    int i = 0;
    int total = records.size();
    fout<<records.size()<<std::endl;
    for(std::vector<Record>::iterator vi = records.begin(); vi != records.end(); vi++){
        fout<<vi->v<<" "<<vi->vl<<" "<<vi->vr<<" "
           <<(vi->v0).V(0)<<" "<<(vi->v0).V(1)<<" "<<(vi->v0).V(2)<<" "
          <<(vi->v1).V(0)<<" "<<(vi->v1).V(1)<<" "<<(vi->v1).V(2)<<std::endl;
        cb(100*i/total,"saving the records to file...:");
    }
    fout.close();

}

ProgressiveMesh::ProgressiveMesh()
{
}
